var gamejs = require("gamejs");
var matchModule = require("js/match");

/**
 * Radar : an object representing a radar. It can detect enemies
 * so the turrets can hit them.
 * The detection area is a circular sector.
 * 
 * @extends Sprite
 */
 
/**
 * Creates a Radar object in the given position.
 * 
 * @param {Match} match The match where this object will work.
 * @param {[number, number]} coords The radar's position, in [x, y] format.
 * @throws {TypeError} If match isn't a Match object or coords isn't in
 * the correct format.
 * @throws {RangeError} If the given coordinates aren't inside the map
 * recovered from the given Match, or if the given coordinates makes the radar
 * image to be out of the map boundaries.
 * 
 * @constructor
 */
var Radar = exports.Radar = function(match, coords) {
	if( ! (match instanceof matchModule.Match)) {
		throw new TypeError("match isn't a valid Match object");
	}
	
	if( ! match.getMap().isInside(coords)) {
		throw new RangeError("Invalid coordinates");
	}
	
	Radar.superConstructor.apply(this, arguments);
	this.currentMatch = match;
	this.center = coords;
	this.centralAngle = 2 * Math.PI;
	this.startAngle = 0;
	this.currentAngle = 0;
	this.level = 0;
	this.type = "radar";
	
	this.upgradeList = [
		{ price : 100, radius : 100, angularVelocity : Math.PI / 2, resaleValue : 70 }, //level 0
		{ price : 75, radius : 200, angularVelocity : Math.PI, resaleValue : 100 } //level 1
	];
	this.saleable = true;

	this.currentRadius = this.getRadius();
	
	this.image = gamejs.image.load(imgPath + "sat.png");
	
	//sets the correct rect
	var dims = this.image.getSize();
	var topLeftCorner = [Math.round(coords[X] - dims[X] / 2), Math.round(coords[Y] - dims[Y] / 2)];
	this.rect = new gamejs.Rect(topLeftCorner, dims);
	
	//sets the obstructive radius
	this.obstructiveRadius = Math.round((dims[X] > dims[Y] ? dims[X] : dims[Y]) / 2);
	
	if(( ! match.getMap().isInside(this.rect.topleft)) || ( ! match.getMap().isInside(this.rect.bottomright))) {
		throw new RangeError("Invalid coordinates(image out of boundaries)");
	}
	
	//radius for collideCircle
	this.radius = this.obstructiveRadius;
};

/**
 * Radar extend Sprite.
 */
gamejs.utils.objects.extend(Radar, gamejs.sprite.Sprite);

/**
 * Returns the bounding rectangle of this radar.
 * 
 * @return {gamejs.Rect} The bounding rectangle.
 */
Radar.prototype.getBoundingRect = function() {
	return this.rect;
};

/**
 * Returns the coordinates of the center of this radar.
 *
 * @returns {[number, number]} The coordinates of the center as vector [x, y].
 */
Radar.prototype.getCenter = function() {
	return this.center;
};

/**
 * Returns the length of the detector ray.
 *
 * @return {number} The length of the ray, in pixel.
 */
Radar.prototype.getRadius = function() {
	return (this.upgradeList[this.level]).radius;
};

/**
 * Returns the angular velocity of the detector ray.
 *
 * @return {number} The angular velocity, in rad/s.
 */
Radar.prototype.getAngularVelocity = function() {
	return (this.upgradeList[this.level]).angularVelocity;
};

/**
 * Returns the level of this radar.
 *
 * @param {number} The level of this radar.
 */
Radar.prototype.getLevel = function() {
	return this.level;
};

/**
 * Returns the amount of credits needed to build this radar.
 *
 * @return {number} The amount of credits needed to build.
 */
Radar.prototype.priceToBuild = function() {
	return (this.upgradeList[0]).price;
};

/**
 * Returns the amount of credits needed to upgrade this radar.
 *
 * @return {number} The amount of credits needed to upgrade.
 * @throws {Error(InvalidOperationError)} If this radar can't be upgraded.
 */
Radar.prototype.priceToUpgrade = function() {
	if( ! this.canUpgrade()) {
		throw new Error("InvalidOperationError : radar can't be upgraded");
	}
	
	return (this.upgradeList[this.level + 1]).price;
};

/**
 * Returns the match where this radar work.
 *
 * @return {Match} The Match object where this radar work.
 */
Radar.prototype.getMatch = function() {
	return this.currentMatch;
};

/**
 * Sets the angle where the detection starts.
 *
 * @param {number} angle The angle where the detection starts.
 * @throws {TypeError} If angle isn't a number.
 * @throws {RangeError} If angle isn't a finite number. 
 */
Radar.prototype.setStartAngle = function(angle) {
	if(typeof angle != "number") {
		throw new TypeError("angle is not a number");
	}
	
	if(isNaN(angle) || ! isFinite(angle)) {
		throw new RangeError("angle is not finite");
	}
	
	this.startAngle = angle % (2 * Math.PI);
	this.currentAngle = 0;
};

/**
 * Sets the angle covered by the radar.
 *
 * @param {number} angle The angle (in radians) covered by the radar, must be in [PI/6 ; 2*PI].
 * @throws {TypeError} If angle isn't a number.
 * @throws {RangeError} If angle's value isn't valid.
 */
Radar.prototype.setCentralAngle = function(angle) {
	if(typeof angle != "number") {
		throw new TypeError("angle is not a number");
	}
	
	if((angle < Math.PI / 6) || (angle > 2 * Math.PI) || isNaN(angle)) {
		throw new RangeError("angle isn't in the correct interval");
	}
	
	this.centralAngle = angle;
};

/**
 * Returns the resale value of the radar.
 *
 * @return {number} The amount of credits refunded when this radar is sold.
 */
Radar.prototype.getResaleValue = function() {
	return this.upgradeList[this.level].resaleValue;
};

/**
 * Tells if the radar can be upgraded.
 *
 * @return {boolean} True if the radar can be upgraded, false otherwise.
 */
Radar.prototype.canUpgrade = function() {
	return (this.level != (this.upgradeList.length - 1));
};

/**
 * Triggers a radar's upgrade.
 * 
 * @throws {Error(InvalidOperationError)} If this radar can't be upgraded
 * or if player doesn't have enough credits.
 */
Radar.prototype.upgrade = function() {
	if( ! this.canUpgrade()) {
		throw new Error("InvalidOperationError : radar can't be upgraded");
	}
	
	var upgradePrice = this.priceToUpgrade();
	if(this.currentMatch.hasCredit(upgradePrice)) {
		this.currentMatch.changeCredit(-upgradePrice);
		this.level += 1;
	}
	else {
		throw new Error("InvalidOperationError : not enough credits");
	}
};

/**
 * Returns the obstructive radius of the radar.
 * 
 * @return {number} The obstructive radius of the radar, in pixel.
 */
Radar.prototype.getObstructiveRadius = function() {
	return this.obstructiveRadius;
};

/**
 * Makes the radar saleable or not.
 *
 * @param {boolean} True to make saleable the radar, false to make
 * it not saleable.
 */
Radar.prototype.setSaleable = function(value) {
	if(value) {
		this.saleable = true;
	}
	else {
		this.saleable = false;
	}
};

/**
 * Tells if the radar is saleable.
 *
 * @return {boolean} True if the radar is saleable, false otherwise.
 */
Radar.prototype.getSaleable = function() {
	return this.saleable;
};

/**
 * Sells the radar, removing it from the map.
 * 
 * @throws {Error(InvalidOperationError)} If the radar isn't present in the map or
 * if it's present but isn't saleable.
 */
Radar.prototype.sell = function() {
	this.currentMatch.getMap().removeRadar(this);
};

/**
 * Updates the position of the detector ray of the radar, and checks
 * for collisions with enemies.
 * 
 * @param {number} msDuration The time past from the last call, in ms.
 */
Radar.prototype.update = function(msDuration) {
	//rotate the ray
	var angVel = this.getAngularVelocity();
	var madeAngle = angVel * (msDuration / 1000);//calculates the angle actually made
	this.currentAngle = this.startAngle + ((this.currentAngle + madeAngle) % this.centralAngle);
	
	var totalAngle = this.startAngle + this.currentAngle;
	
	//cut the ray if it collides with an obstacle, a radar or a turret ->
	//checks all the point of the ray and sets this.currentRadius
	var maxRadius = this.getRadius();
	var minRadius = this.getObstructiveRadius() + 1;
	var checkRadius = 0;
	var currentMap = this.currentMatch.getMap();
	var alreadySet = false;
	
	for(checkRadius = minRadius; checkRadius <= maxRadius; checkRadius++) {
		var checkPointX = Math.round(this.center[X] + Math.sin(totalAngle) * checkRadius);
		var checkPointY = Math.round(this.center[Y] + Math.cos(totalAngle) * checkRadius);
		var checkPoint = [checkPointX, checkPointY];
		
		if(currentMap.isInside(checkPoint) && currentMap.isFilled(checkPoint)) {
			checkRadius - 1;
			break;
		}
	}
	
	this.currentRadius = checkRadius;
	
	//checks for collision with enemies and sets detected flag, if needed
	var startPoint = this.center;
	
	var endPointX = Math.round(startPoint[X] + Math.sin(totalAngle) * this.currentRadius);
	var endPointY = Math.round(startPoint[Y] + Math.cos(totalAngle) * this.currentRadius);
	var endPoint = [endPointX, endPointY];
	currentMap.getEnemies().forEach(function(currentEnemy) {
		if(currentEnemy.getBoundingRect().collideLine(startPoint, endPoint)) {
			currentEnemy.setDetected(true);
		}
	});
};

/**
 * Draws the radar in the given Surface.
 * 
 * @param {gamejs.Surface} surface The Surface where draw on.
 */
Radar.prototype.draw = function(surface) {
	//draws the image
	surface.blit(this.image, this.rect.topleft);
	
	//draws the detector ray
	var startPoint = this.center;
	var totalAngle = this.startAngle + this.currentAngle;
	var endPointX = Math.round(startPoint[X] + Math.sin(totalAngle) * this.currentRadius);
	var endPointY = Math.round(startPoint[Y] + Math.cos(totalAngle) * this.currentRadius);
	var endPoint = [endPointX, endPointY];
	gamejs.draw.line(surface, '#33FF00', startPoint, endPoint, 2);
};